package environment.dungeons;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import temperature.Temperature;
import temperature.TemperatureUnit;

import environment.Direction;

import static org.junit.Assert.*;

public class TeleportableSquareTest {

	private TeleportableSquare teleportableSquare1;
	private TeleportableSquare teleportableSquare2;
	private TeleportableSquare teleportableSquare3;
	private TeleportableSquare teleportableSquare4;
	private TeleportableSquare teleportableSquare5;
	private static NormalSquare square1;
	private static NormalSquare square2;
	private static NormalSquare square3;
	private static NormalSquare square4;
	private static NormalSquare square5;
	private static NormalSquare square6;
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		square1 = new NormalSquare(Temperature.CELSIUS_0, BigDecimal.ZERO);
		square2 = new NormalSquare(new Temperature(BigDecimal.TEN, TemperatureUnit.CELSIUS), BigDecimal.TEN);
		square3 = new NormalSquare(new Temperature(new BigDecimal(20), TemperatureUnit.CELSIUS), new BigDecimal(20));
		square4 = new NormalSquare(new Temperature(new BigDecimal(30), TemperatureUnit.CELSIUS), new BigDecimal(30));
		square5 = new NormalSquare(new Temperature(new BigDecimal(40), TemperatureUnit.CELSIUS), new BigDecimal(40));
		square6 = new NormalSquare(new Temperature(new BigDecimal(50), TemperatureUnit.CELSIUS), new BigDecimal(50));
		square1.setStateUsed();
		square2.setStateUsed();
		square3.setStateUsed();
		square4.setStateUsed();
		square5.setStateUsed();
		square6.setStateUsed();
		square1.setNeighbourInDirection(square2, Direction.SOUTH);
		square1.setNeighbourInDirection(square3, Direction.NORTH);
		square1.setNeighbourInDirection(square4, Direction.WEST);
		square1.setNeighbourInDirection(square5, Direction.EAST);
		square2.setNeighbourInDirection(square6, Direction.SOUTH);

	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
		teleportableSquare1 = new NormalSquare();
		teleportableSquare2 = new NormalSquare(Temperature.CELSIUS_1, new BigDecimal(99));
		teleportableSquare3 = new NormalSquare(Temperature.CELSIUS_0, new BigDecimal(0));
		teleportableSquare4 = new TransparentSquare(Direction.FLOOR);
		teleportableSquare5 = new TransparentSquare(Direction.EAST);
		teleportableSquare1.setStateUsed();
		teleportableSquare2.setStateUsed();
		teleportableSquare3.setStateUsed();
		teleportableSquare4.setStateUsed();
		teleportableSquare5.setStateUsed();
		teleportableSquare1.setTeleportable(true);
		teleportableSquare4.setTeleportable(true);
		teleportableSquare5.setTeleportable(true);
		teleportableSquare1.addAsTeleportationSquare(teleportableSquare2);
		teleportableSquare1.addAsTeleportationSquare(teleportableSquare3);
		teleportableSquare1.addAsTeleportationSquare(teleportableSquare4);
		teleportableSquare1.addAsTeleportationSquare(teleportableSquare5);
		teleportableSquare4.addAsTeleportationSquare(teleportableSquare5);
		teleportableSquare5.addAsTeleportationSquare(teleportableSquare4);

	}

	@After
	public void tearDown() throws Exception {
	}
	
	@Test
	public void calcTemperature() {
		assertTrue(square1.calcTemperature().compareTo(new Temperature(new BigDecimal(25), TemperatureUnit.CELSIUS)) == 0);
	}
	
	@Test
	public void calcHumidity() {
		assertTrue(square1.calcHumidity().compareTo(new BigDecimal(25)) == 0);
	}

	@Test
	public void getRandomTeleportationSquare() {
		for(int i = 0; i < 10000; i++) {
			TeleportableSquare random = teleportableSquare1.getRandomTeleportationSquare();
			assertTrue(teleportableSquare1.hasAsTeleportationSquare(random));
			assertFalse(random.isTerminated());
			assertSame(teleportableSquare4.getRandomTeleportationSquare(), teleportableSquare5);
		}
	}
	
	@Test
	public void getNbTeleportationSquares() {
		assertTrue(teleportableSquare1.getNbTeleportationSquares() == 4);
		teleportableSquare2.terminate();
		assertTrue(teleportableSquare1.getNbTeleportationSquares() == 3);
		teleportableSquare1.setTeleportable(false);
		assertTrue(teleportableSquare1.getNbTeleportationSquares() == 0);
	}
	
	@Test
	public void getTeleportationSquareAt() {
		assertSame(teleportableSquare2, teleportableSquare1.getTeleportationSquareAt(1));
		assertSame(teleportableSquare3, teleportableSquare1.getTeleportationSquareAt(2));
		assertSame(teleportableSquare4, teleportableSquare1.getTeleportationSquareAt(3));
		assertSame(teleportableSquare5, teleportableSquare1.getTeleportationSquareAt(4));
		assertSame(teleportableSquare5, teleportableSquare4.getTeleportationSquareAt(1));
		assertSame(teleportableSquare4, teleportableSquare5.getTeleportationSquareAt(1));
		teleportableSquare2.terminate();
		assertSame(teleportableSquare3, teleportableSquare1.getTeleportationSquareAt(1));
		assertSame(teleportableSquare4, teleportableSquare1.getTeleportationSquareAt(2));
		assertSame(teleportableSquare5, teleportableSquare1.getTeleportationSquareAt(3));
	}
	
	@Test
	public void canHaveAsTeleportationSquareAt() {
		TeleportableSquare tele = new TransparentSquare(Direction.EAST);
		assertFalse(teleportableSquare1.canHaveAsTeleportationSquare(tele));
		assertFalse(tele.canHaveAsTeleportationSquare(teleportableSquare3));
		tele.setTeleportable(true);
		assertTrue(tele.canHaveAsTeleportationSquare(teleportableSquare3));
		tele.setStateUsed();
		assertTrue(teleportableSquare1.canHaveAsTeleportationSquare(tele));
		assertFalse(teleportableSquare1.canHaveAsTeleportationSquare(teleportableSquare1));
		assertTrue(teleportableSquare1.canHaveAsTeleportationSquareAt(tele, 5));
		teleportableSquare2.terminate();
		assertTrue(teleportableSquare1.canHaveAsTeleportationSquareAt(tele, 4));
		assertFalse(teleportableSquare1.canHaveAsTeleportationSquareAt(tele, 5));
		assertFalse(teleportableSquare1.canHaveAsTeleportationSquareAt(teleportableSquare3, 2));
		assertTrue(teleportableSquare1.canHaveAsTeleportationSquareAt(teleportableSquare3, 1));
	}
	
	@Test
	public void hasAsTeleportationSquare() {
		assertTrue(teleportableSquare1.hasAsTeleportationSquare(teleportableSquare2));
		assertFalse(teleportableSquare1.hasAsTeleportationSquare(square1));
		teleportableSquare2.terminate();
		assertFalse(teleportableSquare1.hasAsTeleportationSquare(teleportableSquare2));
		assertFalse(square1.hasAsTeleportationSquare(teleportableSquare1));
	}
	
	@Test
	public void hasTeleportationSquares() {
		assertTrue(teleportableSquare1.hasTeleportationSquares());
		assertTrue(teleportableSquare4.hasTeleportationSquares());
		assertTrue(teleportableSquare5.hasTeleportationSquares());
		assertFalse(square1.hasTeleportationSquares());
		assertFalse(square6.hasTeleportationSquares());
		teleportableSquare1.setTeleportable(false);
		assertFalse(teleportableSquare1.hasTeleportationSquares());
	}
	
	@Test
	public void getTeleportationSquares() {
		List<TeleportableSquare> allTeleportationSquares = new ArrayList<TeleportableSquare>();
		teleportableSquare3.terminate();
		teleportableSquare5.terminate();
		allTeleportationSquares.add(teleportableSquare2);
		allTeleportationSquares.add(teleportableSquare4);
		assertEquals(allTeleportationSquares, teleportableSquare1.getTeleportationSquares());
		assertTrue(teleportableSquare2.getTeleportationSquares().isEmpty());
	}
	
	@Test
	public void addAsTeleportationSquare() {
		TeleportableSquare tele = new TransparentSquare(Direction.EAST);
		tele.setStateUsed();
		tele.setTeleportable(true);
		teleportableSquare1.addAsTeleportationSquare(tele);
		assertSame(tele, teleportableSquare1.getTeleportationSquareAt(5));
		assertTrue(teleportableSquare1.getNbTeleportationSquares() == 5);
		try {
			teleportableSquare1.addAsTeleportationSquare(tele);
			assert false;
		} catch (Throwable e) {
			assert true;
		}
	}
	
	@Test
	public void removeAsTeleportationSquare() {
		teleportableSquare1.removeAsTeleportationSquare(teleportableSquare3);
		assertSame(teleportableSquare2, teleportableSquare1.getTeleportationSquareAt(1));
		assertSame(teleportableSquare4, teleportableSquare1.getTeleportationSquareAt(2));
		assertSame(teleportableSquare5, teleportableSquare1.getTeleportationSquareAt(3));
		assertTrue(teleportableSquare1.getNbTeleportationSquares() == 3);
		assertFalse(teleportableSquare1.hasAsTeleportationSquare(teleportableSquare3));
		teleportableSquare4.removeAsTeleportationSquare(teleportableSquare5);
		assertTrue(teleportableSquare4.getNbTeleportationSquares() == 0);
		assertTrue(teleportableSquare4.isTeleportable());
		teleportableSquare4.terminate();
		try {
			teleportableSquare1.removeAsTeleportationSquare(teleportableSquare4);
			assert false;
		} catch (Throwable e) {
			assert true;
		}
		assertSame(teleportableSquare2, teleportableSquare1.getTeleportationSquareAt(1));
		assertSame(teleportableSquare5, teleportableSquare1.getTeleportationSquareAt(2));
		assertTrue(teleportableSquare1.getNbTeleportationSquares() == 2);
		assertFalse(teleportableSquare1.hasAsTeleportationSquare(teleportableSquare4));
		teleportableSquare1.removeAsTeleportationSquare(teleportableSquare5);
		assertSame(teleportableSquare2, teleportableSquare1.getTeleportationSquareAt(1));
		assertTrue(teleportableSquare1.getNbTeleportationSquares() == 1);
		assertFalse(teleportableSquare1.hasAsTeleportationSquare(teleportableSquare5));
	}
	
	@Test
	public void setTeleportable() {
		teleportableSquare1.setTeleportable(true);
		assertTrue(teleportableSquare1.isTeleportable());
		List<TeleportableSquare> allTeleportationSquares = new ArrayList<TeleportableSquare>();
		allTeleportationSquares.add(teleportableSquare2);
		allTeleportationSquares.add(teleportableSquare3);
		allTeleportationSquares.add(teleportableSquare4);
		allTeleportationSquares.add(teleportableSquare5);
		assertEquals(allTeleportationSquares, teleportableSquare1.getTeleportationSquares());
		teleportableSquare1.setTeleportable(false);
		assertEquals(new ArrayList<TeleportableSquare>(), teleportableSquare1.getTeleportationSquares());
		assertFalse(teleportableSquare1.isTeleportable());
		teleportableSquare1.setTeleportable(false);
		assertFalse(teleportableSquare1.isTeleportable());
		teleportableSquare1.setTeleportable(true);
		assertTrue(teleportableSquare1.isTeleportable());
		assertEquals(new ArrayList<TeleportableSquare>(), teleportableSquare1.getTeleportationSquares());
	}
}
